#ifndef MODEL_HPP
# define MODEL_HPP

# include <vector>
# include <string>
# include <map>
# include <GL/gl.h>
# include <GL/glu.h>

# include "Color.hpp"
# include "AnimStates.hpp"
# include "Resource.hpp"
# include "IClock.hpp"

namespace gdl
{

  ////////////////////////////////////////////////////////////
  /// Forward declaration of ModelImpl.
  ////////////////////////////////////////////////////////////
  class ModelImpl;

  ////////////////////////////////////////////////////////////
  /// Forward declaration of AnimConfigImpl.
  ////////////////////////////////////////////////////////////
  class AnimConfigImpl;

  ////////////////////////////////////////////////////////////
  /// The ModelImpl class provide the gestion of your model.
  ////////////////////////////////////////////////////////////
  class Model : private Resource<ModelImpl>
  {
  private:
    bool CheckUniqueAnimation();

  public:
    ////////////////////////////////////////////////////////////
    /// Default constructror.
    ////////////////////////////////////////////////////////////
    Model(void);
    ////////////////////////////////////////////////////////////
    /// Default destructror.
    ////////////////////////////////////////////////////////////
    ~Model(void);

    ////////////////////////////////////////////////////////////
    /// Load the model.
    ///
    /// \param[in] filename Filename with the extension.
    /// \return the Model.
    ////////////////////////////////////////////////////////////
    static Model	load(std::string const & filename);

    ////////////////////////////////////////////////////////////
    /// Update the animation of the model.
    ///
    /// \param[in] gameTime Game's clock.
    ////////////////////////////////////////////////////////////
    void	update(IClock const & gameTime);

    ////////////////////////////////////////////////////////////
    /// Draw the model in the Window.
    ////////////////////////////////////////////////////////////
    void	draw(void);

    ////////////////////////////////////////////////////////////
    /// Draw the model in a specific pose.
    ///
    /// \param[in] pose_id ID of the specific pose.
    ////////////////////////////////////////////////////////////
    void	draw_pose(int pose_id);

    ////////////////////////////////////////////////////////////
    /// Do nothing.
    /// Have to display informations about the Model
    /// like stack anime number, vertices number...
    ////////////////////////////////////////////////////////////
    void	infos(void);

    ////////////////////////////////////////////////////////////
    /// Play the animation.
    ///
    /// \param[in] _name Animation name.
    /// \param[in] state (AnimState enum).
    /// \return If successful, true is returned.
    /// Otherwise, false is returned.
    ////////////////////////////////////////////////////////////
    bool	play(std::string const & _name, char state = Anim::RUN);

    ////////////////////////////////////////////////////////////
    /// Check if the animation has started.
    ///
    /// \param name Animation name.
    /// \return If successful, true is returned.
    /// Otherwise, false is returned.
    ////////////////////////////////////////////////////////////
    bool	animation_hasStarted(std::string const & name) const;
    ////////////////////////////////////////////////////////////
    /// Check if the animation is ended.
    ///
    /// \param[in] name Animation name.
    /// \return If the animation has ended, true is returned.
    /// Otherwise, false is returned.
    ////////////////////////////////////////////////////////////
    bool	anim_is_ended(std::string const & name) const;

    ////////////////////////////////////////////////////////////
    /// Stop the animation.
    ///
    /// \param[in] name Animation name.
    ////////////////////////////////////////////////////////////
    void	stop_animation(std::string const & name);

    ////////////////////////////////////////////////////////////
    /// Get the animation speed.
    ///
    /// \param[in] name Animation name.
    /// \return If the animation doesn't exist, -1 is returned.
    ////////////////////////////////////////////////////////////
    double	get_anim_speed(std::string const & name) const;

    ////////////////////////////////////////////////////////////
    /// Get the blend factor.
    ///
    /// \param[in] name Animation name.
    /// \return If the animation doesn't exist, -1 is returned.
    ////////////////////////////////////////////////////////////
    double	get_anim_blendfactor(std::string const & name) const;

    ////////////////////////////////////////////////////////////
    /// Get the anim state.
    ///
    /// \param[in] name Animation name.
    /// \return If the animation doesn't exist, -1 is returned.
    ////////////////////////////////////////////////////////////
    char	get_anim_state(std::string const & name) const;

    ////////////////////////////////////////////////////////////
    /// Set the blend factor.
    ///
    /// \param[in] name Animation name.
    /// \param[in] blendFactor Blend factor.
    ////////////////////////////////////////////////////////////
    void	set_anim_bendfactor(std::string const & name, double blendFactor);

    ////////////////////////////////////////////////////////////
    /// Set the animation speed.
    ///
    /// \param[in] name Animation name.
    /// \param[in] speed Speed of the animation.
    ////////////////////////////////////////////////////////////
    void	set_anim_speed(std::string const & name, double speed);

    ////////////////////////////////////////////////////////////
    /// Get the default model color.
    ///
    /// \return The default color.
    ////////////////////////////////////////////////////////////
    Color const &	get_default_model_color(void) const;

    ////////////////////////////////////////////////////////////
    /// Set the default model color.
    ///
    /// \param[in] color The default color.
    ////////////////////////////////////////////////////////////
    void		set_default_model_color(Color const & color);

    ////////////////////////////////////////////////////////////
    /// Set the anim state.
    ///
    /// \param[in] name Animation name.
    /// \param[in] state (with AnimState enum).
    ////////////////////////////////////////////////////////////
    void	set_anim_state(std::string const & name, char state);

    ////////////////////////////////////////////////////////////
    /// Add an animation state.
    ///
    /// \param[in] name Animation name.
    /// \param[in] state (in Anim namespace).
    ////////////////////////////////////////////////////////////
    void	add_anim_state(std::string const & name, Anim::AnimStates state);

    ////////////////////////////////////////////////////////////
    /// Remove an animation state.
    ///
    /// \param[in] name Animation name.
    /// \param[in] state (in Anim namespace).
    ////////////////////////////////////////////////////////////
    void	remove_anim_state(std::string const & name, Anim::AnimStates state);

    ////////////////////////////////////////////////////////////
    /// Get the stack of the animation's name.
    ///
    /// \return Vector of strings.
    ////////////////////////////////////////////////////////////
    std::vector<std::string> const& get_stackanimation_names(void) const;

    ////////////////////////////////////////////////////////////
    /// Get the animation's name.
    ///
    /// \return Vector of strings.
    ////////////////////////////////////////////////////////////
    std::vector<std::string> const& get_animation_names(void) const;
    
    ////////////////////////////////////////////////////////////
    /// Cut the animation.
    ///
    /// \param[in] _model The model.
    /// \param[in] stackAnim The animations stack.
    /// \param[in] id_start First id.
    /// \param[in] id_end Last id.
    /// \param[in] _newName Name of the new animation.
    /// \return If successfull, true is returned.
    /// Otherwise, false is returned.
    ////////////////////////////////////////////////////////////
    static bool	cut_animation(Model& _model,
			      std::string const & stackAnim,
			      int id_start,
			      int id_end,
			      std::string const & _newName);


    ////////////////////////////////////////////////////////////
    /// Open the scope to draw models.
    ////////////////////////////////////////////////////////////
    static void Begin(void);
    ////////////////////////////////////////////////////////////
    /// Close the scope to draw models.
    ////////////////////////////////////////////////////////////
    static void End(void);

    using Resource<ModelImpl>::isValid;

  private:

    typedef std::map<std::string, AnimConfigImpl*>	AnimationsType;
    AnimationsType					m_animations; // Animation type

    ////////////////////////////////////////////////////////////
    /// Explicit constructor.
    ///
    /// \param impl The pointer on ModelImpl.
    ////////////////////////////////////////////////////////////
    explicit Model(ModelImpl* impl);

    //    void	isStatic(bool status);

    ////////////////////////////////////////////////////////////
    /// To reach private members of ModelImpl.
    ////////////////////////////////////////////////////////////
    friend class ModelImpl;

    ////////////////////////////////////////////////////////////
    /// To reach private members of ResourceManagerImpl.
    ////////////////////////////////////////////////////////////
    friend class ResourceManagerImpl;
  };

}

#endif /* !MODEL_HPP */
